"""
Configuration command implementation.

Provides configuration management capabilities including viewing,
editing, validating, and managing CodeMCP configuration files.
"""

import argparse
import json
from typing import Dict, Any
from pathlib import Path

from ...core.config import Config, load_config
from ...core.error_handler import CodeMCPError, ConfigurationError
from ..base_command import CLICommand


class ConfigCommand(CLICommand):
    """Command for managing CodeMCP configuration."""
    
    def add_parser(self, subparsers) -> argparse.ArgumentParser:
        """Add config command parser."""
        parser = subparsers.add_parser(
            'config',
            help='Manage CodeMCP configuration',
            description="""
Manage CodeMCP configuration files including viewing current settings,
editing configuration values, and validating configuration files.
            """.strip(),
            formatter_class=argparse.RawDescriptionHelpFormatter,
            epilog="""
Examples:
  codemcp config --show
  codemcp config --set llm.provider=anthropic
  codemcp config --validate
  codemcp config --init
            """.strip()
        )
        
        # Action options (mutually exclusive)
        action_group = parser.add_mutually_exclusive_group(required=True)
        action_group.add_argument(
            '--show', '-s',
            action='store_true',
            help='Show current configuration'
        )
        action_group.add_argument(
            '--set',
            type=str,
            metavar='KEY=VALUE',
            help='Set configuration value (e.g., llm.provider=openai)'
        )
        action_group.add_argument(
            '--get',
            type=str,
            metavar='KEY',
            help='Get specific configuration value'
        )
        action_group.add_argument(
            '--validate', '-v',
            action='store_true',
            help='Validate configuration file'
        )
        action_group.add_argument(
            '--init',
            action='store_true',
            help='Initialize default configuration file'
        )
        action_group.add_argument(
            '--edit',
            action='store_true',
            help='Open configuration file in editor'
        )
        
        # Additional options
        parser.add_argument(
            '--config-file', '-f',
            type=str,
            help='Configuration file to operate on'
        )
        parser.add_argument(
            '--global', '-g',
            action='store_true',
            dest='global_config',
            help='Use global configuration file'
        )
        parser.add_argument(
            '--local', '-l',
            action='store_true',
            help='Use local configuration file'
        )
        parser.add_argument(
            '--format',
            choices=['toml', 'json', 'yaml'],
            default='toml',
            help='Configuration file format (default: toml)'
        )
        
        return parser
    
    async def execute(self, args: argparse.Namespace) -> int:
        """Execute the config command."""
        try:
            # Determine config file path
            config_path = self._get_config_path(args)
            
            # Execute the requested action
            if args.show:
                return await self._show_config(config_path, args)
            elif args.set:
                return await self._set_config_value(config_path, args.set, args)
            elif args.get:
                return await self._get_config_value(config_path, args.get, args)
            elif args.validate:
                return await self._validate_config(config_path, args)
            elif args.init:
                return await self._init_config(config_path, args)
            elif args.edit:
                return await self._edit_config(config_path, args)
            
            return 0
            
        except CodeMCPError as e:
            self.printer.print_error(f"Configuration error: {e.message}")
            return 1
        except Exception as e:
            self.printer.print_error(f"Unexpected error: {e}")
            return 1
    
    def _get_config_path(self, args: argparse.Namespace) -> Path:
        """Determine the configuration file path to use."""
        if args.config_file:
            return Path(args.config_file)
        
        if args.global_config:
            # Global config in user's home directory
            global_paths = [
                Path.home() / ".config" / "codemcp" / "config.toml",
                Path.home() / ".codemcp.toml"
            ]
            
            for path in global_paths:
                if path.exists():
                    return path
            
            # Return first global path for creation
            return global_paths[0]
        
        if args.local:
            # Local config in current directory
            local_paths = [
                Path.cwd() / "codemcp.toml",
                Path.cwd() / ".codemcp.toml"
            ]
            
            for path in local_paths:
                if path.exists():
                    return path
            
            return local_paths[0]
        
        # Auto-detect existing config file
        search_paths = [
            Path.cwd() / "codemcp.toml",
            Path.cwd() / ".codemcp.toml",
            Path.home() / ".config" / "codemcp" / "config.toml",
            Path.home() / ".codemcp.toml"
        ]
        
        for path in search_paths:
            if path.exists():
                return path
        
        # Default to local config
        return Path.cwd() / "codemcp.toml"
    
    async def _show_config(self, config_path: Path, args: argparse.Namespace) -> int:
        """Show current configuration."""
        self.printer.print_header("CodeMCP Configuration", level=1)
        
        if config_path.exists():
            self.printer.print_info(f"Configuration file: {config_path}")
            
            try:
                config = load_config(str(config_path), validate=False)
                config_dict = config.to_dict()
                
                # Display configuration sections
                self._display_config_section("Server", config_dict.get("server", {}))
                self._display_config_section("Languages", config_dict.get("languages", {}))
                self._display_config_section("Documents", config_dict.get("documents", {}))
                self._display_config_section("LLM", config_dict.get("llm", {}))
                self._display_config_section("Manual Mode", config_dict.get("manual_mode", {}))
                self._display_config_section("Cache", config_dict.get("cache", {}))
                self._display_config_section("Logging", config_dict.get("logging", {}))
                
                # Show custom settings
                custom_settings = {k: v for k, v in config_dict.items() 
                                 if k not in ["server", "languages", "documents", "llm", 
                                            "manual_mode", "cache", "logging", "debug", "project_root"]}
                if custom_settings:
                    self._display_config_section("Custom Settings", custom_settings)
                
                # Show top-level settings
                top_level = {k: v for k, v in config_dict.items() 
                           if k in ["debug", "project_root"] and v is not None}
                if top_level:
                    self._display_config_section("General", top_level)
                
            except Exception as e:
                self.printer.print_error(f"Error loading configuration: {e}")
                return 1
        else:
            self.printer.print_warning(f"Configuration file not found: {config_path}")
            self.printer.print_info("Use 'codemcp config --init' to create a default configuration")
        
        return 0
    
    def _display_config_section(self, section_name: str, section_data: Dict[str, Any]):
        """Display a configuration section."""
        if not section_data:
            return
        
        self.printer.print_header(section_name, level=2)
        
        for key, value in section_data.items():
            if isinstance(value, dict):
                self.printer.print_colored(f"  {key}:", Color.BRIGHT_WHITE)
                for subkey, subvalue in value.items():
                    self.printer.print_key_value(f"    {subkey}", str(subvalue))
            else:
                # Mask sensitive values
                display_value = self._mask_sensitive_value(key, value)
                self.printer.print_key_value(f"  {key}", str(display_value))
        
        print()
    
    def _mask_sensitive_value(self, key: str, value: Any) -> str:
        """Mask sensitive configuration values."""
        sensitive_keys = ['api_key', 'password', 'secret', 'token']
        
        if any(sensitive in key.lower() for sensitive in sensitive_keys):
            if value and str(value).strip():
                return "********"
            else:
                return "(not set)"
        
        return str(value)
    
    async def _set_config_value(self, config_path: Path, key_value: str, args: argparse.Namespace) -> int:
        """Set a configuration value."""
        try:
            # Parse key=value
            if '=' not in key_value:
                raise CodeMCPError("Invalid format. Use KEY=VALUE (e.g., llm.provider=openai)")
            
            key, value = key_value.split('=', 1)
            key = key.strip()
            value = value.strip()
            
            # Load existing config or create new
            if config_path.exists():
                config = load_config(str(config_path), validate=False)
            else:
                config = Config()
            
            # Set the value
            self._set_nested_value(config, key, value)
            
            # Validate the updated configuration
            try:
                config.validate()
            except ConfigurationError as e:
                self.printer.print_warning(f"Configuration validation warning: {e.message}")
                
                confirm = input("Continue anyway? (y/N): ").strip().lower()
                if confirm not in ['y', 'yes']:
                    self.printer.print_info("Configuration update cancelled")
                    return 1
            
            # Save the configuration
            config_path.parent.mkdir(parents=True, exist_ok=True)
            config.save_to_file(str(config_path))
            
            self.printer.print_success(f"Configuration updated: {key} = {value}")
            self.printer.print_info(f"Saved to: {config_path}")
            
        except Exception as e:
            self.printer.print_error(f"Failed to set configuration: {e}")
            return 1
        
        return 0
    
    def _set_nested_value(self, config: Config, key: str, value: str):
        """Set a nested configuration value."""
        # Parse nested key (e.g., "llm.provider" -> ["llm", "provider"])
        key_parts = key.split('.')
        
        # Convert value to appropriate type
        converted_value = self._convert_value(value)
        
        # Navigate to the correct section
        if len(key_parts) == 1:
            # Top-level setting
            if hasattr(config, key_parts[0]):
                setattr(config, key_parts[0], converted_value)
            else:
                config.custom_settings[key_parts[0]] = converted_value
        elif len(key_parts) == 2:
            # Section.setting
            section_name, setting_name = key_parts
            
            if hasattr(config, section_name):
                section = getattr(config, section_name)
                if hasattr(section, setting_name):
                    setattr(section, setting_name, converted_value)
                else:
                    raise CodeMCPError(f"Unknown setting: {key}")
            else:
                raise CodeMCPError(f"Unknown section: {section_name}")
        else:
            raise CodeMCPError(f"Unsupported nested key depth: {key}")
    
    def _convert_value(self, value: str) -> Any:
        """Convert string value to appropriate type."""
        # Boolean values
        if value.lower() in ['true', 'yes', 'on', '1']:
            return True
        elif value.lower() in ['false', 'no', 'off', '0']:
            return False
        
        # Numeric values
        try:
            if '.' in value:
                return float(value)
            else:
                return int(value)
        except ValueError:
            pass
        
        # String value
        return value
    
    async def _get_config_value(self, config_path: Path, key: str, args: argparse.Namespace) -> int:
        """Get a specific configuration value."""
        if not config_path.exists():
            self.printer.print_error(f"Configuration file not found: {config_path}")
            return 1
        
        try:
            config = load_config(str(config_path), validate=False)
            value = self._get_nested_value(config, key)
            
            # Mask sensitive values
            display_value = self._mask_sensitive_value(key, value)
            
            self.printer.print_info(f"{key} = {display_value}")
            
        except Exception as e:
            self.printer.print_error(f"Failed to get configuration value: {e}")
            return 1
        
        return 0
    
    def _get_nested_value(self, config: Config, key: str) -> Any:
        """Get a nested configuration value."""
        key_parts = key.split('.')
        
        if len(key_parts) == 1:
            # Top-level setting
            if hasattr(config, key_parts[0]):
                return getattr(config, key_parts[0])
            elif key_parts[0] in config.custom_settings:
                return config.custom_settings[key_parts[0]]
            else:
                raise CodeMCPError(f"Unknown setting: {key}")
        elif len(key_parts) == 2:
            # Section.setting
            section_name, setting_name = key_parts
            
            if hasattr(config, section_name):
                section = getattr(config, section_name)
                if hasattr(section, setting_name):
                    return getattr(section, setting_name)
                else:
                    raise CodeMCPError(f"Unknown setting: {key}")
            else:
                raise CodeMCPError(f"Unknown section: {section_name}")
        else:
            raise CodeMCPError(f"Unsupported nested key depth: {key}")
    
    async def _validate_config(self, config_path: Path, args: argparse.Namespace) -> int:
        """Validate configuration file."""
        if not config_path.exists():
            self.printer.print_error(f"Configuration file not found: {config_path}")
            return 1
        
        try:
            self.printer.print_info(f"Validating configuration: {config_path}")
            config = load_config(str(config_path), validate=True)
            
            self.printer.print_success("Configuration is valid!")
            
            # Show summary
            self.printer.print_info("Configuration summary:")
            self.printer.print_key_value("  Server Port", config.server.port)
            self.printer.print_key_value("  LLM Provider", config.llm.provider)
            self.printer.print_key_value("  Cache Type", config.cache.type)
            self.printer.print_key_value("  Log Level", config.logging.level)
            
        except ConfigurationError as e:
            self.printer.print_error(f"Configuration validation failed: {e.message}")
            return 1
        except Exception as e:
            self.printer.print_error(f"Error validating configuration: {e}")
            return 1
        
        return 0
    
    async def _init_config(self, config_path: Path, args: argparse.Namespace) -> int:
        """Initialize default configuration file."""
        if config_path.exists():
            self.printer.print_warning(f"Configuration file already exists: {config_path}")
            
            overwrite = input("Overwrite existing file? (y/N): ").strip().lower()
            if overwrite not in ['y', 'yes']:
                self.printer.print_info("Configuration initialization cancelled")
                return 0
        
        try:
            # Create default configuration
            config = Config()
            
            # Ensure parent directory exists
            config_path.parent.mkdir(parents=True, exist_ok=True)
            
            # Save configuration
            config.save_to_file(str(config_path))
            
            self.printer.print_success(f"Default configuration created: {config_path}")
            self.printer.print_info("You can now edit the configuration with:")
            self.printer.print_info(f"  codemcp config --edit")
            
        except Exception as e:
            self.printer.print_error(f"Failed to initialize configuration: {e}")
            return 1
        
        return 0
    
    async def _edit_config(self, config_path: Path, args: argparse.Namespace) -> int:
        """Open configuration file in editor."""
        import os
        import subprocess
        
        if not config_path.exists():
            self.printer.print_error(f"Configuration file not found: {config_path}")
            self.printer.print_info("Use 'codemcp config --init' to create a configuration file")
            return 1
        
        # Determine editor
        editor = (
            os.environ.get('EDITOR') or 
            os.environ.get('VISUAL') or 
            'nano'  # Default fallback
        )
        
        try:
            self.printer.print_info(f"Opening {config_path} in {editor}")
            subprocess.run([editor, str(config_path)], check=True)
            
            # Validate after editing
            self.printer.print_info("Validating edited configuration...")
            try:
                load_config(str(config_path), validate=True)
                self.printer.print_success("Configuration is valid!")
            except ConfigurationError as e:
                self.printer.print_warning(f"Configuration validation warning: {e.message}")
            
        except subprocess.CalledProcessError:
            self.printer.print_error(f"Failed to open editor: {editor}")
            return 1
        except FileNotFoundError:
            self.printer.print_error(f"Editor not found: {editor}")
            self.printer.print_info("Set the EDITOR environment variable to your preferred editor")
            return 1
        
        return 0